package com.handinfo.engine.resource;

import java.io.*;
import java.util.Vector;

import com.handinfo.engine.GameManager;
import com.handinfo.util.Tools;
import com.xl.lib.Utils;

/**
 * @author Xia Mingwei
 * @version 1.0
 */
public class ResourceManager
{
	public static final String __STORE_NAME = "Game1_v1.0";
	private static final int __STORE_MAX_CAPACITY = 512 << 10; // 512KB
	private static final int __STORE_MAX_SINGLE_CAPACITY = 100 << 10; // 50KB

	public static ResourceManager getInstance()
	{
		if (s_instance == null) {
			s_instance = new ResourceManager();
			s_instance.m_store = new RMSSerialize();
			s_instance.m_store.setMaxCapacity(__STORE_MAX_CAPACITY);
			s_instance.m_store.setMaxSingleCapacity(__STORE_MAX_SINGLE_CAPACITY);
		}
		return s_instance;
	}

	public void setGameResDir(String root)
	{
		m_resDir = root;
	}

	public StringBuffer getGameResDir()
	{
		StringBuffer buff = new StringBuffer(m_resDir);
		return buff;
	}

	public void loadJarPkgResList()
	{
		m_jarResList.clean();
		StringBuffer dir = getGameResDir();
		dir.append("res.list");
		InputStream is = openStream(dir.toString());
		if (is == null)
			return;
		try {
			byte[] buff = Tools.readInputStream(new DataInputStream(is));
//			new byte[is.available()];
//			is.read(buff);
			String file = new String(buff, "utf8");
			int pre = 0, last = 0;
			String res;
			ResHead head;
			while ((last = file.indexOf(0x0D, pre)) >= 0) {
				res = file.substring(pre, last);
				pre = last + 1;

				head = new ResHead();
				int pos1 = 0, pos2 = 0;
				pos2 = res.indexOf("%", pos1);
				head.m_type = (byte) Integer
						.parseInt(res.substring(pos1, pos2));

				pos1 = pos2 + 1;
				pos2 = res.indexOf("%", pos1);
				head.m_url = res.substring(pos1, pos2);

				head.m_version = Integer.parseInt(res.substring(pos2 + 1));
				m_jarResList.put(head);
			}
		}
		catch (IOException ex) {
			Tools.debug(ex);
		}
	}

	public ResourceList filterDownloadResList(ResourceList list)
	{
		ResourceList request = new ResourceList();
		list.reset();
		ResHead head;
		m_store.openSerialize(__STORE_NAME, true, true);
		while (list.hasMoreElements()) {
			head = list.nextElement();
			switch (head.m_type) {
				default: {
					boolean exist = m_store.exist(head);
					if (!exist && !m_jarResList.exist(head)) {
						request.put(head);
					}
					break;
				}
			}

		}
		m_store.closeSerialize();
		return request;
	}

	/**
	 * 根据ResourceList生成Resource
	 * 
	 * @param list
	 * ResourceList
	 * @return Resource
	 */
	public Resource loadResource(Resource res, ResourceList list)
	{
		ResHead head;
		/**
		 * 必须以依赖项先构造,例如顺序为： Image、SptSet、Animation、Map、Scene
		 */
//		Serializeable inst;
		while (list.hasMoreElements()) {
			head = list.nextElement();
			loadResource(res, head);
//			inst = getSerializeObject(res, head);
//			if (inst != null) {
//				res.put(head, inst);
//			}
		}
		return res;
	}

	/**
	 * 根据head载入资源到res中
	 * 
	 * @param res
	 * @param head
	 * @return
	 * @throws IOException
	 */
	public Serializeable loadResource(Resource res, ResHead head)
	{
		Serializeable inst = res.getResource(head);
		if (inst != null)
			return inst;
		inst = getSerializeObject(res, head);
		if (inst != null) {
			res.put(head, inst);
		}
		return inst;
	}
	
	/**
	 * 
	 * 
	 * @param res
	 * @param head
	 * @param data
	 * @return
	 */
	public Serializeable loadResource(Resource res, ResHead head, byte[] data)
	{
		Serializeable inst = res.getResource(head);
		if (inst != null)
			return inst;
		inst = ObjectAdapter.convert(res, head, data);
		if (inst != null) {
			res.put(head, inst);
		}
		else {
			res.addMissRes(head);
			addMissRes(head);
		}
		return inst;
	}

	/**
	 * 从res中移除指定head的资源
	 * 
	 * @param res
	 * @param head
	 */
	public void removeResource(Resource res, ResHead head)
	{
		res.remove(head);
	}

	/**
	 * 从res中移除list中的所有资源
	 * 
	 * @param res
	 * @param list
	 */
	public void removeResource(Resource res, ResourceList list)
	{
		ResHead head;
		while (list.hasMoreElements()) {
			head = list.nextElement();
			res.remove(head);
		}
	}

	/**
	 * 移除res中的所有资源
	 * 
	 * @param res
	 */
	public void removeAllResource(Resource res)
	{
		res.clean();
	}
	
	/**
	 * 根据list更新res
	 * 
	 * @param old
	 * @param list
	 */
	public void updateResource(Resource res, ResourceList list)
	{
		ResourceList rxor = res.rxor(list); // 查找出res中的数据未列在list名单上的，用在后面删除
		ResourceList xor = res.xor(list); // 查找出res中在list名单上没有的数据， 用在加载新数据
		list.reset();
		ResHead head;
		while (rxor.hasMoreElements()) {
			head = rxor.nextElement();
			res.remove(head);
		}
//		Serializeable inst = null;
		int length = xor.size();
		int index = 0;
		while (xor.hasMoreElements()) {
			head = xor.nextElement();
			loadResource(res, head);
			index++;
			m_load_percent = index * 100 / length;
//			inst = getSerializeObject(res, head);
//			if (inst != null)
//				res.put(head, inst);
		}
		m_load_percent = 100;
	}
	public int m_load_percent = 0;

	/**
	 * 根据head获取对象并且放入到res中
	 * 
	 * @param res
	 * @param head
	 * @return
	 * @throws IOException
	 */
	public Serializeable getSerializeObject(Resource res, ResHead head)
	{
		byte[] data = null;
		switch (head.m_type) {
			case ObjectAdapter.RESOURCE_TYPE_ANIM:
			case ObjectAdapter.RESOURCE_TYPE_BG:
			case ObjectAdapter.RESOURCE_TYPE_SCENE:
			case ObjectAdapter.RESOURCE_TYPE_PNG: {
				if (head.m_version <= ResHead.BASE_VERSION) {

					InputStream is = openStream(head.m_url);
					//Tools.debugPrintln("getSerializeObject!!!!!:" + head.m_url);
					if (is == null) {
						m_store.openSerialize(__STORE_NAME, true, false);
						if (m_store.exist(head)) {
							data = m_store.readResource(head);
						}
						else {
							//将欠缺资源添加进对应res以及ResourceManager的欠缺资源列表中
							res.addMissRes(head);
							addMissRes(head);
						}
						m_store.closeSerialize();
					}
					else {
//						if (head.m_type == ObjectAdapter.RESOURCE_TYPE_PNG) {
//							Serializeable inst = ObjectAdapter.convertPng(res, is);
//							if (inst != null) {
//								return inst;
//							} else {
//								res.addMissRes(head);
//								addMissRes(head);
//								return null;
//							}
//						}
						try {
//							int size = is.available();
							data = Tools.readInputStream(new DataInputStream(is));
							is.close();
						}
						catch (Exception ex) {
							Tools.debugPrintln("getSerializeObject异常：" + head.m_url);
							Tools.debug(ex);
						}
					}
				}
				else {
					m_store.openSerialize(__STORE_NAME, true, false);
					if ( m_store.exist(head)) {
						data = m_store.readResource(head);
					}
					else {
						// 将欠缺资源添加进对应res以及ResourceManager的欠缺资源列表中
						res.addMissRes(head);
						addMissRes(head);
//						InputStream is = openStream(head.m_url);
//						if (is == null) {
//							// 将欠缺资源添加进对应res以及ResourceManager的欠缺资源列表中
//							res.addMissRes(head);
//							addMissRes(head);
//						}
//						else {
//							try {
//								int size = is.available();
//								data = new byte[size];
//								is.read(data);
//								is.close();
//							}
//							catch (Exception ex) {
//								Tools.debugPrintln("getSerializeObject异常：" + head.m_url);
//								Tools.debug(ex);
//							}
//						}
					}
					m_store.closeSerialize();
				}
				break;
			}
		}

		if (data != null) {
			Serializeable inst = ObjectAdapter.convert(res, head, data);
			if (inst != null) {
				return inst;
			}
			else {
				res.addMissRes(head);
				addMissRes(head);
				return null;
			}
		}
		else {
			return null;
		}
	}
	
	private int m_writeErrorNum = 0;
	/**
	 * 根据head和data，往存储介质里写入资源
	 * 
	 * @param head
	 * @param data
	 */
	public void downloadResource(ResHead head, byte[] data)
	{
		try {
			switch (head.m_type) {
				case ObjectAdapter.RESOURCE_TYPE_ANIM:
				case ObjectAdapter.RESOURCE_TYPE_BG:
				case ObjectAdapter.RESOURCE_TYPE_PNG:
//				case ObjectAdapter.RESOURCE_TYPE_SCENE:
					m_store.openSerialize(__STORE_NAME, true, false);
					m_store.writeResource(head, data);
					m_store.closeSerialize();
					//如果写入RMS成功，则将m_writeErrorNum清零
					m_writeErrorNum = 0;
					break;
			}
		}
		catch (Exception ex) {
			//累计写入错误数目
			m_writeErrorNum++;
			//如果连续10次写入都异常，则RMS很有可能出问题了，清理之
			if (m_writeErrorNum > 10) {
				m_store.clear();
				m_writeErrorNum = 0;
				Tools.debugPrintln("资源RMS异常，清理掉！~~~~~~~~~~~~");
			}
			Tools.debug(ex);
		}
	}

	/**
	 * 根据file得到对应文件的二进制流（只针对本地文件）
	 * 
	 * @param file
	 * @return
	 */
	public InputStream openStream(String file)
	{
		StringBuffer buff = getGameResDir();
		buff.append(file);
		return Utils.getResourceAsStream(buff.toString());
//		return buff.getClass().getResourceAsStream(buff.toString());
	}

	/**
	 * 根据head得到对应文件或者RMS记录的二进制流
	 * 
	 * @param head
	 * @return
	 */
//	public InputStream openStream(ResHead head)
//	{
//		byte[] data = null;
//		InputStream is = null;
//		switch (head.m_type) {
//			default: {
//				m_store.openSerialize(__STORE_NAME, true, true);
//				if (m_store.exist(head)) {
//					data = m_store.readResource(head);
//					ByteArrayInputStream bais = new ByteArrayInputStream(data);
//				}
//				else {
//					is = openStream(head.m_url);
//				}
//				m_store.closeSerialize();
//				break;
//			}
//		}
//		return is;
//	}

	/**
	 * 移除欠缺资源
	 * 
	 * @param head
	 */
	public void removeMissRes(ResHead head)
	{
		m_res_miss.removeElement(head);
	}

	/**
	 * 添加欠缺资源
	 * 
	 * @param head
	 */
	public void addMissRes(ResHead head)
	{
		if (!m_res_miss.contains(head)) { // 当前m_res_miss没有才添加进去, 并且发送请求资源
			Tools.debugPrintln("添加缺失资源的URL=" + head.m_url);
			m_res_miss.addElement(head);
			requestResource(head);
		}
	}

	/**
	 * 清空欠缺资源列表
	 */
	public void cleanMissResList()
	{
		m_res_miss.removeAllElements();
	}

	/**
	 * 是否包含指定欠缺资源
	 * 
	 * @param head
	 * @return
	 */
	public boolean containMissRes(ResHead head)
	{
		return m_res_miss.contains(head);
	}

	/**
	 * 根据head请求资源
	 * 
	 * @param head
	 */
	public void requestResource(ResHead head)
	{
		if (head != null) {
			GameManager.getInstance().requestResource(head);
		}
	}

	/**
	 * 根据res中的m_res_miss请求资源
	 */
	public void requestResources()
	{
		GameManager.getInstance().requestResource(m_res_miss);
	}

	protected String m_resDir = "";
	private static ResourceManager s_instance;
	protected ResourceSerialize m_store;
	private ResourceList m_jarResList = new ResourceList();
	private Vector m_res_miss = new Vector(); // 欠缺资源列表
	
	/**
	 * 返回是否有缺失资源
	 * @return
	 */
    public int getResMissSize(){
    	return m_res_miss.size();
    }
    
	private ResourceManager()
	{
	}
}
